#!/usr/bin/env coffee

import DB from '../db/sql.coffee'
import axios from '@rmw/axios'
import compact2string from 'compact2string'
import string2compact from 'string2compact'
import {FS} from '../const/dir.coffee'
import {join} from 'path'
import {readFileSync} from 'fs'
import shuffle from 'lodash/shuffle'
import chunk from 'lodash/chunk'
import CONFIG from '@rmw/config'
import Client from './client.coffee'

ip_list = =>
  try
    {data} = await axios.get(
      "https://cdn.jsdelivr.net/gh/rmw-link/nkn_boot/ip"
      {
        timeout:3000
        responseType:'arraybuffer'
        retry:0
      }
    )
    return compact2string.multi data
  catch err
    console.error err
    return compact2string.multi readFileSync(join(FS,'ip'))

OK = DB.prepare "INSERT INTO nkn_boot (addr,delay) VALUES(?,?) ON CONFLICT(addr) DO UPDATE SET ok=excluded.ok+1,delay=(excluded.delay+delay)/2,rank=(1000000*(ok+100)/(100+ok+err))/delay,utime=?"

ERR = DB.prepare "UPDATE nkn_boot SET ok=ok-1,err=err+1,rank=rank/2 WHERE addr=?"

NKN_TIMEOUT = CONFIG.nkn_timeout = CONFIG.nkn_timeout or 4000

now = =>
  new Date()-0

export default (seed)=>

  connected = false
  connecting = 0

  await_connect = (bin)=>
    ip = compact2string bin
    ++ connecting
    new Promise(
      (resolve, reject)=>
        client = Client(seed,ip)
        timeout = setTimeout(
          =>
            -- connecting
            if connected
              client.close()
              ERR.run(bin)
              if connecting == 0
                DB.exec("DELETE FROM nkn_boot WHERE rank<=0;VACUUM;")
          60000
        )

        _now = now()
        client.onConnect(
          =>
            -- connecting
            clearTimeout timeout
            current = parseInt _now/1000
            delay = now()-_now
            #{node} = client.defaultClient
            #if node
            #  if li.length < limit
            #    OK.run string2compact(node.rpcAddr), delay, current
            OK.run bin, delay, current
            #clearTimeout timer
            resolve client
        )
    )

  limit = 32
  delay_default = 99999
  loop
    li = (
      i.addr for i in await DB.prepare(
        'SELECT addr FROM nkn_boot WHERE rank>0 ORDER BY rank DESC,id asc LIMIT '+limit
      ).all()
    )

    if li.length == 0
      _now = parseInt now()/1000
      for ip from await ip_list()
        ip = string2compact(ip)
        li.push ip
        OK.run ip,delay_default,_now
    else
      li = shuffle li

    failed = []

    await_li = []
    for bin_li in chunk(li,6)
      for bin in bin_li
        await_li.push await_connect bin
      client = await new Promise (resolve)=>
        timer = setTimeout(
          =>
            resolve()
          NKN_TIMEOUT
        )
        resolve await Promise.race await_li
        clearTimeout timer

      if client
        connected = true
        return client



